import { writeFileSync } from 'node:fs';
import { XMLBuilder } from 'xmlbuilder2/lib/interfaces';
import { create } from 'xmlbuilder2';
import { UserConfig } from 'vite';
import { resolve } from 'node:path';
import {
  PrerenderContentFile,
  PrerenderSitemapConfig,
  SitemapConfig,
} from './options';

export type PagesJson = {
  page: string;
  lastMod: string;
  changefreq?: string;
  priority?: string;
};

export async function buildSitemap(
  config: UserConfig,
  sitemapConfig: SitemapConfig,
  routes: (string | undefined)[] | (() => Promise<(string | undefined)[]>),
  outputDir: string,
  routeSitemaps: Record<
    string,
    PrerenderSitemapConfig | (() => PrerenderSitemapConfig) | undefined
  >,
) {
  const routeList: string[] = await optionHasRoutes(routes);

  if (routeList.length) {
    const slash = checkSlash(sitemapConfig.host || '');
    const sitemapData: PagesJson[] = routeList.map((page: string) => {
      const url = `${slash}${page.replace(/^\/+/g, '')}`;
      const config = routeSitemaps[url];
      const props = typeof config === 'object' ? config : config?.();

      return {
        page: `${sitemapConfig.host}${url}`,
        lastMod: props?.lastmod ?? new Date().toISOString().split('T')[0],
        changefreq: props?.changefreq,
        priority: props?.priority,
      };
    });

    const sitemap = createXml('urlset');

    for (const item of sitemapData) {
      const page = sitemap.ele('url');
      page.ele('loc').txt(item.page);
      page.ele('lastmod').txt(item.lastMod);

      if (item.changefreq) {
        page.ele('changefreq').txt(item.changefreq);
      }

      if (item.priority) {
        page.ele('priority').txt(item.priority);
      }
    }

    const mapPath = `${resolve(outputDir)}/sitemap.xml`;
    try {
      console.log(`Writing sitemap at ${mapPath}`);
      writeFileSync(mapPath, sitemap.end({ prettyPrint: true }));
    } catch (e) {
      console.error(`Unable to write file at ${mapPath}`, e);
    }
  }
}

function createXml(elementName: 'urlset' | 'sitemapindex'): XMLBuilder {
  return create({ version: '1.0', encoding: 'UTF-8' })
    .ele(elementName, {
      xmlns: 'https://www.sitemaps.org/schemas/sitemap/0.9',
    })
    .com(`This file was automatically generated by Analog.`);
}

function checkSlash(host: string): string {
  const finalChar = host.slice(-1);
  return finalChar === '/' ? '' : '/';
}

async function optionHasRoutes(
  routes: (string | undefined)[] | (() => Promise<(string | undefined)[]>),
): Promise<string[]> {
  let routeList: (string | undefined)[];

  if (typeof routes === 'function') {
    // returns an array or undefined
    routeList = await routes();
  } else if (Array.isArray(routes)) {
    // returns an array of strings
    routeList = routes;
  } else {
    // default it to an empty of array
    routeList = [];
  }

  return routeList.filter(Boolean) as string[];
}
